package com.lyy.lottery.service.impl;

import com.lyy.lottery.common.errorcode.ServiceErrorCodeConstants;
import com.lyy.lottery.common.exception.ServiceException;
import com.lyy.lottery.common.util.JacksonUtil;
import com.lyy.lottery.common.util.RedisUtil;
import com.lyy.lottery.controller.param.CreatePrizeActivityParam;
import com.lyy.lottery.controller.param.CreateUserActivityParam;
import com.lyy.lottery.controller.param.PageParam;
import com.lyy.lottery.controller.result.CreateActivityParam;
import com.lyy.lottery.dao.dataobject.ActivityDo;
import com.lyy.lottery.dao.dataobject.ActivityPrizeDo;
import com.lyy.lottery.dao.dataobject.ActivityUserDo;
import com.lyy.lottery.dao.mapper.*;
import com.lyy.lottery.service.ActivityService;
import com.lyy.lottery.service.dto.ActivityDTO;
import com.lyy.lottery.service.dto.ActivityDetailDTO;
import com.lyy.lottery.service.dto.CreateActivityDTO;
import com.lyy.lottery.service.dto.PageListDTO;
import com.lyy.lottery.service.enums.ActivityPrizeStatusEnum;
import com.lyy.lottery.service.enums.ActivityPrizeTiersEnum;
import com.lyy.lottery.service.enums.ActivityStatusEnum;
import com.lyy.lottery.service.enums.ActivityUserStatusEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.lyy.lottery.dao.dataobject.PrizeDo;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

@Slf4j
@Service
public class ActivityServiceImpl implements ActivityService {

    private final String ACTIVITY_PREFIX="ACTIVITY_";
    private final long ACTIVITY_TIMEOUT=60 * 60 * 24 * 3;//秒--3天

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private PrizeMapper prizeMapper;

    @Autowired
    private ActivityMapper activityMapper;

    @Autowired
    private ActivityPrizeMapper activityPrizeMapper;

    @Autowired
    private ActivityUserMapper activityUserMapper;

    @Autowired
    private RedisUtil redisUtil;

    //创建活动
    @Override
    @Transactional(rollbackFor = Exception.class)//设及多表，保证本地事务-回滚
    public CreateActivityDTO createActivity(CreateActivityParam param) {
        //1、校验活动有效性
        checkActivityInfo(param);

        //2、保存活动基本信息--activityDo---activity表(activity_name,description,status)
        ActivityDo activityDo=new ActivityDo();
        activityDo.setActivityName(param.getActivityName());
        activityDo.setDescription(param.getDescription());
        activityDo.setStatus(ActivityStatusEnum.RUNNING.name());
        activityMapper.insert(activityDo);
        log.info("活动id:{}",activityDo.getId());

        //3、保存活动关联的奖品信息--activityPrizeDoList--activity-prize表(activity_id,prize_id,prize_amount,prize_tiers,status)
        List<CreatePrizeActivityParam> prizeActivityParams=param.getActivityPrizeList();
        List<ActivityPrizeDo> activityPrizeDoList=prizeActivityParams.stream()
                .map(prizeParm -> {
                    ActivityPrizeDo activityPrizeDo=new ActivityPrizeDo();
                    activityPrizeDo.setActivityId(activityDo.getId());
                    activityPrizeDo.setPrizeAmount(prizeParm.getPrizeAmount());
                    activityPrizeDo.setPrizeId(prizeParm.getPrizeId());
                    activityPrizeDo.setPrizeTiers(prizeParm.getPrizeTiers());
                    activityPrizeDo.setStatus(ActivityPrizeStatusEnum.INIT.name());
                    return activityPrizeDo;
                }).collect(Collectors.toList());
        activityPrizeMapper.batchInsert(activityPrizeDoList);

        //3、保存活动关联的人员信息--activityUserDos--activity_user表(activity_id,user_id,user_name,status)
        List<CreateUserActivityParam> userActivityParams=param.getActivityUserList();
        List<ActivityUserDo> activityUserDos=userActivityParams.stream()
                .map(userParam ->{
                    ActivityUserDo activityUserDo=new ActivityUserDo();
                    activityUserDo.setActivityId(activityDo.getId());
                    activityUserDo.setStatus(ActivityUserStatusEnum.INIT.name());
                    activityUserDo.setUserId(userParam.getUserId());
                    activityUserDo.setUserName(userParam.getUserName());
                    return activityUserDo;
                }).collect(Collectors.toList());
        activityUserMapper.batchInsert(activityUserDos);

        //4、整合完整的活动信息存放 redis

        //1)获取奖品基本属性列表--prizeDOList
        // ① 获取需要查询的奖品id列表
        List<Long> prizeIds = param.getActivityPrizeList().stream()
                .map(CreatePrizeActivityParam::getPrizeId)
                .distinct()
                .collect(Collectors.toList());
        //② 根据奖品id列表查询活动信息列表 PrizeDo{ name, imageUrl, price, description }
        List<PrizeDo> prizeDOList = prizeMapper.batchSelectByIds(prizeIds);

        //2)缓存完整的活动信息()
        ActivityDetailDTO detailDTO = convertToActivityDetailDTO(activityDo, activityUserDos,
                prizeDOList, activityPrizeDoList);
        cacheActivity(detailDTO);

        //构造返回，返回 activityId
        CreateActivityDTO createActivityDTO=new CreateActivityDTO();
        createActivityDTO.setActivityId(activityDo.getId());
        return createActivityDTO;
    }

    @Override
    public void cacheActivity(Long activityId) {
        if (null == activityId){
            log.warn("要缓存的活动id为空！");
            throw new ServiceException(ServiceErrorCodeConstants.CACHE_ACTIVITY_ID_IS_EMPTY);
        }
        //查询表：活动表、关联人员、关联奖品、奖品表
        ActivityDo aDo=activityMapper.selectById(activityId);
        if (null == aDo){
            log.error("要缓存的活动id有误！");
            throw new ServiceException(ServiceErrorCodeConstants.CACHE_ACTIVITY_ID_ERROR);
        }
        List<ActivityUserDo> auDoList=activityUserMapper.selectByActivityId(activityId);
        List<ActivityPrizeDo> apDoList=activityPrizeMapper.selectByActivityId(activityId);
        List<Long> prizeIds=apDoList.stream().map(ActivityPrizeDo::getPrizeId).collect(Collectors.toList());
        List<PrizeDo> pDoList=prizeMapper.batchSelectByIds(prizeIds);

        //整合活动信息存放redis
        cacheActivity(convertToActivityDetailDTO(aDo,auDoList,pDoList,apDoList));
    }

    @Override
    public PageListDTO<ActivityDTO> findActivityList(PageParam param) {
        //获取总量
        int total=activityMapper.count();

        //获取当前列表页
        List<ActivityDo> activityDoList=activityMapper.selectActivity(param.getOffset(),param.getPageSize());

        List<ActivityDTO> activityDTOList=activityDoList.stream()
                .map(activityDo -> {
                    ActivityDTO activityDTO=new ActivityDTO();
                    activityDTO.setActivityId(activityDo.getId());
                    activityDTO.setActivityName(activityDo.getActivityName());
                    activityDTO.setDescription(activityDo.getDescription());
                    activityDTO.setStatus(ActivityStatusEnum.forName(activityDo.getStatus()));
                    return activityDTO;
                }).collect(Collectors.toList());

        return new PageListDTO<>(total,activityDTOList);
    }

    @Override
    public ActivityDetailDTO getActivityDetail(Long activityId) {
        if (null == activityId){
            log.info("查询活动详细信息失败，activityId为空！");
            return null;
        }

        //1、查询redis
        ActivityDetailDTO detailDTO=getActivityFromCache(activityId);
        if (null != detailDTO){
            log.info("查询活动详细信息成功！detailDTO:{}",JacksonUtil.writeValueAsString(detailDTO));
            return detailDTO;
        }

        //2、若redis中不存在（过期/存入失败），则查询数据库

        //活动基本信息
        ActivityDo activityDo=activityMapper.selectById(activityId);

        //关联奖品
        List<ActivityPrizeDo> apDoList=activityPrizeMapper.selectByActivityId(activityId);

        //关联人员
        List<ActivityUserDo> auDoList=activityUserMapper.selectByActivityId(activityId);

        //奖品表：先获取要查询的奖品id
        List<Long> prizeIds=apDoList.stream()
                .map(ActivityPrizeDo::getPrizeId)
                .collect(Collectors.toList());
        List<PrizeDo> pDoList=prizeMapper.batchSelectByIds(prizeIds);

        //整合活动详细信息，存放redis
        detailDTO=convertToActivityDetailDTO(activityDo,auDoList,pDoList,apDoList);
        cacheActivity(detailDTO);

        //返回
        return detailDTO;
    }

    private void checkActivityInfo(CreateActivityParam param) {
        if (null==param){
            throw new ServiceException(ServiceErrorCodeConstants.CREATE_ACTIVITY_INFO_IS_EMPTY);
        }
        //1、userId 是否存在--user表
        List<Long> userIds=param.getActivityUserList()
                .stream()
                .map(CreateUserActivityParam::getUserId)
                .distinct()
                .collect(Collectors.toList());

        List<Long> existUserIds=userMapper.selectExistByIds(userIds);
        if (CollectionUtils.isEmpty(existUserIds)){
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_USER_ERROR);
        }

        userIds.forEach(id->{
            if (!existUserIds.contains(id)){
                throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_USER_ERROR);
            }
        });

        //2、prizeId是否存在--prize表
        List<Long> prizeIds=param.getActivityPrizeList()
                .stream()
                .map(CreatePrizeActivityParam::getPrizeId)
                .distinct()
                .collect(Collectors.toList());

        List<Long> existPrizeIds=prizeMapper.selectExistByIds(prizeIds);
        if (CollectionUtils.isEmpty(existPrizeIds)){
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_USER_ERROR);
        }
        prizeIds.forEach(id->{
            if (!existPrizeIds.contains(id)){
                throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRIZE_ERROR);
            }
        });

        //3、要求：人员数量 >= 奖品数量
        int userAmount=param.getActivityUserList().size();
        long prizeAmount =param.getActivityPrizeList()
                .stream()
                .mapToLong(CreatePrizeActivityParam::getPrizeAmount)
                .sum();
        if (userAmount<prizeAmount){
            throw new ServiceException(ServiceErrorCodeConstants.USER_PRIZE_AMOUNT_ERROR);
        }

        //4、校验活动奖品有效性
        param.getActivityPrizeList().forEach(prize -> {
            if (null == ActivityPrizeTiersEnum.forName(prize.getPrizeTiers())){
                throw new ServiceException(ServiceErrorCodeConstants.USER_PRIZE_TIERS_ERROR);
            }
        });
    }

    private ActivityDetailDTO convertToActivityDetailDTO(ActivityDo activityDo,
                                                         List<ActivityUserDo> activityUserDos,
                                                         List<PrizeDo> prizeDOList,
                                                         List<ActivityPrizeDo> activityPrizeDoList) {
                //从 ActivityDo 获取活动基本信息
                ActivityDetailDTO detailDTO=new ActivityDetailDTO();
                detailDTO.setActivityId(activityDo.getId());
                detailDTO.setActivityName(activityDo.getActivityName());
                detailDTO.setDesc(activityDo.getDescription());
                detailDTO.setStatus(ActivityStatusEnum.forName(activityDo.getStatus()));

                //从 List<ActivityPrizeDo> 获取活动关联奖品信息
                List<ActivityDetailDTO.PrizeDTO> prizeDTOList=activityPrizeDoList
                        .stream()
                        .map(apDO ->{
                            ActivityDetailDTO.PrizeDTO prizeDTO=new ActivityDetailDTO.PrizeDTO();
                            prizeDTO.setPrizeId(apDO.getPrizeId());

                            Optional<PrizeDo> optionalPrizeDo=prizeDOList.stream()
                                            .filter(prizeDo -> prizeDo.getId().equals(apDO.getPrizeId()))
                                                    .findFirst();
                            //如果PrizeDO为空，不执行当前方法，不为空才执行
                            optionalPrizeDo.ifPresent(prizeDo -> {
                                prizeDTO.setName(prizeDo.getName());
                                prizeDTO.setImageUrl(prizeDo.getImageUrl());
                                prizeDTO.setPrice(prizeDo.getPrice());
                                prizeDTO.setDescription(prizeDo.getDescription());
                            });

                            prizeDTO.setTiers(ActivityPrizeTiersEnum.forName(apDO.getPrizeTiers()));
                            prizeDTO.setPrizeAmount(apDO.getPrizeAmount());
                            prizeDTO.setStatus(ActivityPrizeStatusEnum.forName(apDO.getStatus()));
                            return prizeDTO;
                        }).collect(Collectors.toList());

                detailDTO.setPrizeDTOList(prizeDTOList);

                //从 List<ActivityUserDo> 获取活动关联人员信息
                List<ActivityDetailDTO.UserDTO> userDTOList=activityUserDos.stream()
                        .map(auDO ->{
                            ActivityDetailDTO.UserDTO userDTO=new ActivityDetailDTO.UserDTO();
                            userDTO.setUserId(auDO.getUserId());
                            userDTO.setUserName(auDO.getUserName());
                            userDTO.setStatus(ActivityUserStatusEnum.forName(auDO.getStatus()));
                            return userDTO;
                        }).collect(Collectors.toList());
                detailDTO.setUserDTOList(userDTOList);
                return detailDTO;
    }

    //缓存完整的活动信息
    //key:ACTIVITY_12
    //value:JacksonUtil.writeValueAsString(detailDTO)
    //timeout：3d
    private void cacheActivity(ActivityDetailDTO detailDTO) {
        if (null==detailDTO || null==detailDTO.getActivityId()) {
            log.warn("要缓存的活动信息不存在！");
            return;
        }
        try{
            redisUtil.set(ACTIVITY_PREFIX+detailDTO.getActivityId(),
                    JacksonUtil.writeValueAsString(detailDTO),
                    ACTIVITY_TIMEOUT);
        }catch (Exception e){
            log.error("缓存活动异常，ActivityDetailDTO={}",
                    JacksonUtil.writeValueAsString(detailDTO),e);
        }
    }



    //根据 活动id 从缓存中获取详细信息
    public ActivityDetailDTO getActivityFromCache(Long activityId){
        if (null == activityId){
            log.warn("获取缓存活动数据的activityId为空！");
            return null;
        }
        try {
            String str=redisUtil.get(ACTIVITY_PREFIX+activityId);
            if (!StringUtils.hasText(str)){
                log.warn("获取的缓存活动数据为空！key={}",ACTIVITY_PREFIX+activityId);
                return null;
            }
            return JacksonUtil.readValue(str,ActivityDetailDTO.class);
        }catch (Exception e){
            log.error("从缓存中获取活动信息异常：key={}",ACTIVITY_PREFIX+activityId,e);
            return null;
        }
    }
}





















